/*
 * ControladorNodosLibres.c
 *      Author: Grupo Klow
 */

#include "ControladorIDsLibres.h"
#include <string.h>
#include <stdlib.h>


void ControladorIDsLibres_guardarBuffer(ControladorIDsLibres * controlador) {
	char * bloque = (char *) malloc(controlador->longitudBloque * 2);
	unsigned int actual = 0;

	memcpy(&bloque[actual],&(controlador->cantidadLibres),sizeof(unsigned int));
	actual = sizeof(unsigned int);


	memcpy(&bloque[actual],&(controlador->cantidadEnBufferIDs),sizeof(unsigned int));
	actual+= sizeof(unsigned int);

	memcpy(&bloque[actual],controlador->bufferIDs,sizeof(Tid) * controlador->cantidadEnBufferIDs);
	actual+=sizeof(Tid) * controlador->cantidadEnBufferIDs;

	//Completo con espacios para que no quede basura con caracteres no escribibles.
	memset(&bloque[actual],' ',controlador->longitudBloque*2 - actual);

	fseek(controlador->handleArchivo,0, SEEK_SET);

	fwrite(bloque,controlador->longitudBloque*2,1,controlador->handleArchivo);
	fflush(controlador->handleArchivo);
	//Libero memoria
	free(bloque);
}

void ControladorIDsLibres_cargarBuffer(ControladorIDsLibres * controlador) {

	char * bloque = (char *) malloc(controlador->longitudBloque * 2);
	unsigned int actual = 0;

	fseek(controlador->handleArchivo,0, SEEK_SET);

	fread(bloque,controlador->longitudBloque*2,1,controlador->handleArchivo);

	memcpy(&(controlador->cantidadLibres),&bloque[actual],sizeof(unsigned int));
	actual = sizeof(unsigned int);


	memcpy(&(controlador->cantidadEnBufferIDs),&bloque[actual],sizeof(unsigned int));
	actual+= sizeof(unsigned int);

	memcpy(controlador->bufferIDs,&bloque[actual],sizeof(Tid) * controlador->cantidadEnBufferIDs);

	//Libero memoria
	free(bloque);
}

char * ControladorIDsLibres_cargarBloque (ControladorIDsLibres * controlador, unsigned int idBloque) {
	unsigned int offset = controlador->longitudBloque * 2;
	char * bloque;

	bloque = (char*) malloc(controlador->longitudBloque);

	offset+=(idBloque - 1) * controlador->longitudBloque;
	// Me posiciono
	fseek(controlador->handleArchivo,offset,SEEK_SET);
	// Escribo
	fread(bloque,controlador->longitudBloque,1,controlador->handleArchivo);
	//Devuelvo el bloque que lei
	return bloque;
}

void ControladorIDsLibres_guardarBloque (ControladorIDsLibres * controlador, unsigned int idBloque, char * bloque) {

	unsigned int offset = controlador->longitudBloque * 2;

	offset+=(idBloque - 1) * controlador->longitudBloque;
	// Me posiciono
	fseek(controlador->handleArchivo,offset,SEEK_SET);
	// Escribo
	fwrite(bloque,controlador->longitudBloque,1,controlador->handleArchivo);
	fflush(controlador->handleArchivo);

}

void ControladorIDsLibres_llenarMitadBufferIDs(ControladorIDsLibres * controlador) {
	unsigned int idBloque = controlador->cantidadLibres / controlador->cantidadIDsBloque;

	char * bloque = ControladorIDsLibres_cargarBloque(controlador,idBloque);

	memcpy(controlador->bufferIDs,bloque ,sizeof(int) * controlador->cantidadIDsBloque);

	controlador->cantidadEnBufferIDs+= controlador->cantidadIDsBloque;

	free(bloque);
}

void ControladorIDsLibres_vaciarMitadBuffer(ControladorIDsLibres * controlador) {
	unsigned int idBloque = controlador->cantidadLibres / controlador->cantidadIDsBloque;
	char * bloque = (char *) malloc(controlador->longitudBloque);

	controlador->cantidadEnBufferIDs-= controlador->cantidadIDsBloque;

	memcpy(bloque,&(controlador->bufferIDs[0]),sizeof(Tid) * controlador->cantidadIDsBloque);

	memcpy(controlador->bufferIDs,&(controlador->bufferIDs[controlador->cantidadMaxBufferIDs - controlador->cantidadIDsBloque + 2]) ,sizeof(Tid) * controlador->cantidadEnBufferIDs);

	ControladorIDsLibres_guardarBloque(controlador,idBloque,bloque);

	free(bloque);
}

void ControladorIDsLibres_Iniciar(ControladorIDsLibres * controlador, char * nombreArchivo, unsigned int longitudBloque) {
	//Me fijo si esta el archivo

	controlador->longitudBloque=longitudBloque;

	controlador->cantidadIDsBloque = controlador->longitudBloque / sizeof(Tid);
	controlador->cantidadMaxBufferIDs = (2 * controlador->cantidadIDsBloque) - 2;

	controlador->bufferIDs= (Tid*) malloc(sizeof(Tid) * controlador->cantidadMaxBufferIDs);

	if ( (controlador->handleArchivo  = fopen(nombreArchivo,"r+"))==NULL) {
		//El archivo no existe
		controlador->handleArchivo = fopen (nombreArchivo, "w+");

		controlador->cantidadLibres=0;
		controlador->cantidadEnBufferIDs=0;


		ControladorIDsLibres_guardarBuffer(controlador);
	} else { //El archivo ya existe
		ControladorIDsLibres_cargarBuffer(controlador);
	}

}

unsigned int ControladorIDsLibres_tieneIDLibre(ControladorIDsLibres * controlador) {
	return (controlador->cantidadLibres>0);
}

void ControladorIDsLibres_addIDLibre (ControladorIDsLibres * controlador, Tid id) {

	// ¿Overflow?
	if (controlador->cantidadEnBufferIDs == controlador->cantidadMaxBufferIDs) {
		ControladorIDsLibres_vaciarMitadBuffer(controlador);
	}

	controlador->bufferIDs[controlador->cantidadEnBufferIDs]= id;
	controlador->cantidadEnBufferIDs++;
	controlador->cantidadLibres++;

	ControladorIDsLibres_guardarBuffer(controlador);
}

unsigned int ControladorIDsLibres_getIDLibre(ControladorIDsLibres * controlador) {
	// ¿Underflow?
	Tid id;

	if (controlador->cantidadEnBufferIDs == 0) {
		ControladorIDsLibres_llenarMitadBufferIDs(controlador);
	}

	id = controlador->bufferIDs[controlador->cantidadEnBufferIDs-1];

	controlador->cantidadEnBufferIDs--;
	controlador->cantidadLibres--;

	ControladorIDsLibres_guardarBuffer(controlador);
	return id;
}

unsigned int * ControladorIDsLibres_getAllLibres(ControladorIDsLibres * controlador, unsigned int * cantidad) {


	unsigned int * retorno ;
	unsigned int cantidadBloques = (controlador->cantidadLibres - controlador->cantidadEnBufferIDs) / controlador->cantidadIDsBloque;
	unsigned int posicion=0;
	char * bloque;
	//Creo el vector

	retorno = (Tid *) malloc(sizeof(Tid) * controlador->cantidadLibres);

	unsigned int i;

	for (i = 1; i <= cantidadBloques; i++) {
		//Leo el bloque
		bloque = ControladorIDsLibres_cargarBloque(controlador,i);
		//Agrego los resultados.
		memcpy(&retorno[posicion],bloque ,sizeof(Tid) * controlador->cantidadIDsBloque);
		//
		posicion+=controlador->cantidadIDsBloque;
		//Libero el bloque leido
		free(bloque);
	}

	//Copio los que estan en el buffer.
	memcpy(&retorno[posicion], controlador->bufferIDs, sizeof(Tid) * controlador->cantidadEnBufferIDs);
	//Cargo la cantidad
	*cantidad = controlador->cantidadLibres;

	return retorno;
}

void ControladorIDsLibres_cerrar(ControladorIDsLibres * controlador) {
	free(controlador->bufferIDs);
	fclose(controlador->handleArchivo);
}

